package com.tsystems.protocol.client;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.tsystems.common.CommonKeys;
import com.tsystems.entities.Passenger;
import com.tsystems.entities.Station;
import com.tsystems.entities.Ticket;
import com.tsystems.entities.TimeTableElement;
import com.tsystems.entities.Train;

/**
 * 
 * @author vlad
 *
 * Класс в котором производится все основное взаимодействие клиента с сервером.
 */
public class ClientPassenger {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private static ClientPassenger instance;

	//Логин под которым вошел пользователь
	private String login;

	//Адрес сервера
	private static final String host = "localhost";
	//Номер порта к которому производится подключение  
	private static final int port = 9090;
	
	
	private ClientConnectionManager connectionService;

	private ClientPassenger(){
		
	}
	
	public static ClientPassenger getInstance() {
		if (instance == null) {
			instance = new ClientPassenger();
		}
		return instance;
	}

	/**
	 * Метод создает новое соединение с сервером.
	 * 
	 * @return true - если клиент успешно установил соединение, иначе - false.
	 */
	public boolean init() {
		try {
			logger.info("init");
			connectionService = ClientConnectionManager.getInstance();
			connectionService.connect(host, port);
			return true;
		} catch (UnknownHostException e) {
			logger.error("UnknownHostException", e);
			ClientConnectionManager.getInstance().closeConnection();
		} catch (IOException e) {
			logger.error("IOException", e);
			ClientConnectionManager.getInstance().closeConnection();
		}

		return false;
	}

	/**
	 * Метод посылает на сервер данные для входа в систему и получает ответ
	 * 
	 * @param login
	 *            - Имя пользователя
	 * @param password
	 *            - Пароль
	 * @return - Сообщение которое пришло от сервера(На данный момент:
	 *         "Вход в систему успешно выполнен" или
	 *         "Неверный пароль или имя пользователя!") или null в случае если
	 *         произошла ошибка.
	 */
	public synchronized String login(String login, String password) {
		try {
			logger.info("LOGIN: login = {}, pasword = {}", login, password);
			connectionService.getOutput().writeObject(CommonKeys.LOGIN);
			connectionService.getOutput().writeObject(login);
			connectionService.getOutput().writeObject(password);

			String result = (String) connectionService.getInput().readObject();

			if (result.equals("Вход в систему успешно выполнен")) {
				logger.info("login success");
				this.login = login;

			} else {
				logger.info("login fail");
			}

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	
	public synchronized String register(Passenger passenger) {
		try {
			logger.info("REGISTER");
			connectionService.getOutput().writeObject(CommonKeys.REGISTER);
			connectionService.getOutput().writeObject(passenger);
			String result = (String)connectionService.getInput().readObject();
			logger.info(result);

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public synchronized HashMap<Train, List<TimeTableElement>> findTrain(
			Station stationA, Station stationB, Date timeA, Date timeB) {
		try {
			logger.info("FIND_TRAIN");
			connectionService.getOutput().writeObject(CommonKeys.FIND_TRAIN);
			connectionService.getOutput().writeObject(stationA);
			connectionService.getOutput().writeObject(stationB);
			connectionService.getOutput().writeObject(timeA);
			connectionService.getOutput().writeObject(timeB);

			HashMap<Train, List<TimeTableElement>> result = (HashMap<Train, List<TimeTableElement>>) connectionService
					.getInput().readObject();

			if (!result.isEmpty()) {
				logger.info("train found");
			} else {
				logger.info("train not found");
			}

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public synchronized List<TimeTableElement> getTimeTable(Station station) {
		try {
			logger.info("GET_TIMETABLE");
			connectionService.getOutput().writeObject(CommonKeys.GET_TIMETABLE);
			connectionService.getOutput().writeObject(station);

			List<TimeTableElement> result = (List<TimeTableElement>) connectionService
					.getInput().readObject();

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public synchronized List<Ticket> viewTickets(/* Passenger passenger */) {
		try {
			logger.info("VIEW_TICKETS");
			connectionService.getOutput().writeObject(CommonKeys.VIEW_TICKETS);
			// connectionService.getOutput().writeObject(passenger);

			List<Ticket> result = (List<Ticket>) connectionService.getInput()
					.readObject();

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	public synchronized String buyTicket(Station fromStation,
			Station toStation, Date fromTime, Train train, Date arrivalTime) {
		try {
			logger.info("BUY_TICKETS");
			connectionService.getOutput().writeObject(CommonKeys.BUY_TICKETS);
			connectionService.getOutput().writeObject(fromStation);
			connectionService.getOutput().writeObject(toStation);
			connectionService.getOutput().writeObject(fromTime);
			connectionService.getOutput().writeObject(train);
			connectionService.getOutput().writeObject(arrivalTime);

			String result = (String) connectionService.getInput().readObject();

			logger.info(result);
			
			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public synchronized List<Train> viewTrains() {
		try {
			logger.info("VIEW_TRAINS");
			connectionService.getOutput().writeObject(CommonKeys.VIEW_TRAINS);

			Object object = connectionService.getInput().readObject();
			List<Train> result = (List<Train>) object;

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}
		return null;

	}

	@SuppressWarnings("unchecked")
	public synchronized List<TimeTableElement> viewTimeTableElements() {
		try {
			logger.info("VIEW_TTE");
			connectionService.getOutput().writeObject(CommonKeys.VIEW_TTE);

			Object object = connectionService.getInput().readObject();
			List<TimeTableElement> result = (List<TimeTableElement>) object;

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}
		return Collections.emptyList();

	}

	@SuppressWarnings("unchecked")
	public synchronized List<TimeTableElement> getArrivalTimeTable(
			Station station) {
		try {
			logger.info("GET_ARRIVAL_TIMETABLE");
			connectionService.getOutput().writeObject(
					CommonKeys.VIEW_ARRIVAL_TTE);
			connectionService.getOutput().writeObject(station);

			List<TimeTableElement> result = (List<TimeTableElement>) connectionService
					.getInput().readObject();

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public synchronized List<Station> viewStations() {
		try {
			logger.info("VIEW_STATIONS");
			connectionService.getOutput().writeObject(CommonKeys.VIEW_STATIONS);

			Object object = connectionService.getInput().readObject();
			List<Station> result = (List<Station>) object;

			return result;
		} catch (IOException e) {
			logger.error("IOException", e);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException", e);
		}
		return null;

	}

	public String getLogin() {
		return login;
	}

	public synchronized void close() {
		try {
			connectionService.getOutput().writeObject(CommonKeys.CLOSE);
		} catch (IOException e) {
			logger.error("IOException", e);
		}
		connectionService.closeConnection();
	}

}
